import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;


public class ClientTest extends JFrame {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private BorderLayout mainLayout;
	private JMenuBar menuBar;
	private JMenu fileMenu;
	private JMenuItem newGame;
	private String username;
	private int myTroopCount;
	private int myTerrCount;
	private BufferedReader in;
	private PrintWriter out;
	private Socket connection;
	private ObjectInputStream obj_in;
	private ObjectOutputStream obj_out;
	private final int DEFAULT_PORT = 2122;
	private final String DEFAULT_TEST_IP = "localhost";
	private final String DEFAULT_IP = "152.3.53.98";
	private MapPanel grid;
	private MyInfoPanel orderUI;
	private Color myColor;
	private Random r = new Random();
	private Map myMap;
	private Font bigFont, regularFont;
	private int turn = 0;
	private ArrayList<Move> moves = new ArrayList<Move>();
	private ArrayList<Attack> attacks = new ArrayList<Attack>();
	private ArrayList<Order> orders = new ArrayList<Order>();
	
	public ClientTest() {
		super("RISC!");
		initComponents();
	}
	
	private void initComponents() {
		mainLayout = new BorderLayout();
		getContentPane().setLayout(mainLayout);
		createMenuBar();
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setSize(800,650);
		this.setVisible(true);
	}
	
	private void createMenuBar() {
		menuBar = new JMenuBar();
		
		fileMenu = new JMenu("Game");
		
		newGame = new JMenuItem(new AbstractAction("New Game") {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent ae) {
				try {
					setupGame();
				} catch (IOException e) {
					System.out.println("Client setup failure: " + e.getMessage() + "\n");
				}
			}
		});
		fileMenu.add(newGame);
		
		menuBar.add(fileMenu);
		setJMenuBar(menuBar);
	}
	
	private void setupGame() throws UnknownHostException, IOException {
		initConnection();
		String testAccept = in.readLine();
		if(testAccept.startsWith("AC:")) {
			while(true) {
				String tempusername = JOptionPane.showInputDialog("Enter your username");
				out.println(tempusername);
				String acceptMsg = in.readLine();
				if(acceptMsg.startsWith("NA:")) {
					username = tempusername;
					acceptMsg = acceptMsg.replace("NA:", "");
					JOptionPane.showMessageDialog(null, acceptMsg);
					String assnColor = in.readLine();
					System.out.println("Read Color line");
					if(assnColor.startsWith("CS:")) {
						assnColor = assnColor.replace("CS:", "");
						assnColor = assnColor.replace(";", "");
						String[] rgb = assnColor.split(",");
						if(rgb.length == 3)
							myColor = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2]));
						else
							myColor = new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256)); // just in case
					}
					break;
				}
				else
					JOptionPane.showMessageDialog(null, "The username entered already exists, please try another.");
			}
			JOptionPane.showMessageDialog(null, "Waiting for more players");
			if(in.readLine().startsWith("MU:"))
				updateMapPanel();
			setupInfoPanel();
			bigFont = new Font("serif", Font.BOLD, 28);
			regularFont = new Font("Time New Roman", Font.PLAIN, 20);
			System.out.println("Read map; begin message handling");
			io.start();
		return;
		} else {
			JOptionPane.showMessageDialog(null, "The connection failed for some reason: ");
			JOptionPane.showMessageDialog(null, testAccept);
			return;
		}
	}
	
	private void updateMapPanel() {
		try {
			myMap = (Map) obj_in.readObject();
			String myInfo = in.readLine();
			if(myInfo.startsWith("PI:")) {
				myInfo = myInfo.replace("PI:", "");
				myInfo = myInfo.replace(";", "");
				String[] temp = myInfo.split(",");
				myTroopCount = Integer.parseInt(temp[0]);
				myTerrCount = Integer.parseInt(temp[1]);
			}
		} catch (ClassNotFoundException | IOException e) {
			System.out.println("Map update failure: " + e.getMessage());
		}
		grid = new MapPanel(myMap);
		getContentPane().add(BorderLayout.CENTER, grid);
		myMap.printMap();
		setTitle(username);
		System.out.println("\n" + username);
		System.out.println("troops: " + myTroopCount);
		System.out.println("terrs: " + myTerrCount);
	}
	
	private void initConnection() throws UnknownHostException, IOException {
		connection = new Socket(DEFAULT_TEST_IP, DEFAULT_PORT);
		in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
		System.out.println("client in made");
		out = new PrintWriter(connection.getOutputStream(), true);
		System.out.println("client out made");
		obj_in = new ObjectInputStream(connection.getInputStream());
		System.out.println("client obj in made");
		obj_out = new ObjectOutputStream(connection.getOutputStream());
		System.out.println("client obj out made");
		System.out.println("Connection established");
	}
	
	private Thread io = new Thread() {
		public void run() {
			handleMessages();
		}
	};
	
	public void handleMessages() {
		String msg;
		int x = 0;
		try {
			while(true) {
				System.out.println("Handled " + x++ + " messages");
				msg = in.readLine();
				if(msg.startsWith("MU:")) {
					msg = msg.replace("MU:", "");
					JOptionPane.showMessageDialog(null, msg);
				} else if(msg.startsWith("MSG:")) {
					JOptionPane.showMessageDialog(null, msg.replace("MSG:", ""));
				} else if(msg.startsWith("ST:")) {
					System.out.println(msg);
					msg = msg.replace("ST:", "");
					int time = Math.abs(Integer.parseInt(msg));
					System.out.println("Time received: " + time);
					orderUI.TurnPanel.turnclock.setMinutes(time);
					orderUI.TurnPanel.turnclock.startTimer();
					JOptionPane.showMessageDialog(null, "Time has started!");
				} else {
					JOptionPane.showMessageDialog(null, msg);
				}
			} 
		} catch (IOException e) {
			System.out.println("Message handling failure: " + e.getMessage());
		}
	}
	
	public void parseUpdates(String list) {
		String[] temp = list.split(";");
		for(String t : temp) {
			//something
		}
		JOptionPane.showMessageDialog(null, "Map updated!");
	}
	
	private void setupInfoPanel() {
		orderUI = new MyInfoPanel();
		this.getContentPane().add(orderUI, BorderLayout.EAST);
		revalidate();
		repaint();
	}
	
	class MapPanel extends JPanel{
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		ArrayList<MyTerritoryPanel> TerritoryGrid;

		public MapPanel(){        
			GridLayout MapPanelLayout = new GridLayout(5, 5);       
			MapPanelLayout.setVgap(1);      
			MapPanelLayout.setHgap(2);
			TerritoryGrid = new ArrayList<MyTerritoryPanel>();
			this.setLayout(MapPanelLayout);
			for (int i = 0; i < 5; i++)
				for (int j = 0; j < 5; j++){
					MyTerritoryPanel t = new MyTerritoryPanel();
					t.setBackground(myColor);  
					t.setlabel(0);
					TerritoryGrid.add(t);
					this.add(t);
				} 
		}
		
		public MapPanel(Map map) {
			GridLayout gbl = new GridLayout(map.getRows(), map.getCols());       
			gbl.setVgap(1);      
			gbl.setHgap(2);
			this.setLayout(gbl);
			MyTerritoryPanel[][] tPanels = new MyTerritoryPanel[map.getRows()][map.getCols()];
			for(int i = 0; i < map.getRows(); i++) {
				for(int j = 0; j < map.getCols(); j++) {
					tPanels[i][j] = new MyTerritoryPanel(map.getTerritory(i, j).GetAmount());
					tPanels[i][j].setBackground(map.getTerritory(i, j).getColor());
					this.add(tPanels[i][j]);
				}
			}
		}
	}
	
	class MyTerritoryPanel extends JPanel{
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		JLabel amount;
		public MyTerritoryPanel(){
			amount = new JLabel();
			this.add(amount);
		}
		
		public MyTerritoryPanel(int s) {
			this();
			setlabel(s);
		}

		public void setlabel(int s){
			amount.setText(String.valueOf(s));
		}
	}
	
   class MyInfoPanel extends JPanel{     
	   /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	MyTurnPanel TurnPanel;
	   MyOrderPanel OrderPanel;
	   
	   public MyInfoPanel(){
    	   this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    	   TurnPanel = new MyTurnPanel();
    	   OrderPanel = new MyOrderPanel();
    	   this.add(TurnPanel);
    	   this.add(OrderPanel);
       } 
	   
	   public void UpdateInfoPanel(){
		   TurnPanel.UpdateTurnPanel();
		   OrderPanel.UpdateOrderPanel();
	   }
   }
   
   class MyTurnPanel extends JPanel{
	   /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	JLabel turnnumber, totalterritories, numterritories, totaltroops, numtroops;
	   Clock turnclock;
	   
	   public MyTurnPanel(){
		   GridLayout TurnPanelLayout = new GridLayout(3, 2);
		   TurnPanelLayout.setVgap(2);
		   TurnPanelLayout.setHgap(2);
		   
		   turnnumber = new JLabel("Turn " + String.valueOf(turn));
		   turnnumber.setFont(bigFont);
		   turnclock = new Clock();
	       totalterritories = new JLabel("Total Territories: ");
	       totalterritories.setFont(regularFont);
	       numterritories = new JLabel(String.valueOf(myTerrCount));
	       totaltroops = new JLabel("Total Troops: ");
	       totaltroops.setFont(regularFont);
	       numtroops = new JLabel(String.valueOf(myTroopCount));
           
           this.add(turnnumber);
           this.add(turnclock);
           this.add(totalterritories);
           this.add(numterritories);
           this.add(totaltroops);
           this.add(numtroops);
	   }
	   public void UpdateTurnPanel(){
		   turnclock.Reset();
		   turnnumber.setText("Turn " + String.valueOf(turn));
		   numterritories.setText(String.valueOf(myTerrCount));
		   numtroops.setText(String.valueOf(myTroopCount));
	   }
   }
   
   class MyOrderPanel extends JPanel{
       /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	Box basebox, labelbox, enterbox;
       JTextField num, sx, sy, dx, dy;
       JButton submitorderbutton, add;
       JList<String> ordertypelist;
       boolean beginattack;
       
	   public MyOrderPanel(){

    	   GridLayout TurnPanelLayout = new GridLayout(7, 2);
 		   TurnPanelLayout.setVgap(1);
 		   TurnPanelLayout.setHgap(5);
 		   this.setLayout(TurnPanelLayout);
 		   
    		JLabel typelabel = new JLabel("Order Type");	
    		JLabel numberlabel = new JLabel("Number");	
    		JLabel sourceXlabel = new JLabel("SourceX");	
    		JLabel sourceYlabel = new JLabel("SourceY");	
    		JLabel destinationXlabel = new JLabel("DestinationX");	
    		JLabel destinationYlabel = new JLabel("DestinationY");  
    		
    		String [] listEntries = {"Move", "Attack"};		
    		ordertypelist = new JList<String>(listEntries);	
    		JScrollPane scroller = new JScrollPane(ordertypelist);
    		scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    		scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    		ordertypelist.setVisibleRowCount(1);
    		beginattack = false;
    		
    		ordertypelist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);	
    		num = new JTextField(5);
    		sx = new JTextField(3);	
    		sy = new JTextField(3);	
    		dx = new JTextField(3);	
    		dy = new JTextField(3);	
    		
    		this.add(typelabel);
    		this.add(ordertypelist);
     		this.add(numberlabel);
    		this.add(num);
    		this.add(sourceXlabel);
    		this.add(sx);
    		this.add(sourceYlabel);
    		this.add(sy);	
    		this.add(destinationXlabel);
    		this.add(dx);
    		this.add(destinationYlabel);
    		this.add(dy);
    		
		    submitorderbutton = new JButton(new AbstractAction("Submit orders") {
		    	public void actionPerformed(ActionEvent ae) {
		    		try {
						obj_out.writeObject(Arrays.asList(moves));
						try {
							Thread.sleep(500L);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						obj_out.writeObject(Arrays.asList(attacks));
		    		System.out.println("Orders sent successfully");
		    			//obj_out.writeObject(orders);
					} catch (IOException e) {
						System.out.println("Order send failure: " + e.getMessage());
						e.printStackTrace();
					}
		    		submitorderbutton.setEnabled(false);
		    		add.setEnabled(false);
		    		moves.clear();
		    		attacks.clear();
		    		//orders.clear();
		    	}
		    }); 
		    //submitorderbutton.addActionListener(new MySubmitOrderListener());
		    add = new JButton(new AbstractAction("Add Order") {
		    	public void actionPerformed(ActionEvent ae) {
		    		String selection = ordertypelist.getSelectedValue();
					 Territory source = myMap.getTerritory((Integer.parseInt(sx.getText()) - 1), (Integer.parseInt(sy.getText()) - 1));
					 Territory destination = myMap.getTerritory((Integer.parseInt(dx.getText()) - 1), (Integer.parseInt(dy.getText()) - 1));
					 int amount = Integer.parseInt(num.getText());
					 if (selection == "Move"){
					   Move newmove = new Move(username, source, destination, amount);
					   System.out.println(newmove.toString());
					   if (newmove.CheckOrder(myMap))
						   //orders.add(newmove);
						   moves.add(newmove);
							 //theClient.GetMap().SetupMap(newmove)
					 }
					 if (selection == "Attack"){
					   Attack newattack = new Attack(username, source, destination, amount);
					   System.out.println(newattack.toString());
					   if(newattack != null) {
						   if (newattack.CheckOrder(myMap)){
							 //orders.add(newattack);  
						     attacks.add(newattack);
						     JOptionPane.showMessageDialog(null, "Your attack was added successfully!");
						   }
					   } else 
						   JOptionPane.showMessageDialog(null, "Your attack is void?");
					 }
		    	}
		    });
            this.add(submitorderbutton);
            this.add(add);
       } 
	   public void UpdateOrderPanel(){
		   beginattack = false;
	   }
	   class MySubmitOrderListener implements ActionListener{
		   public void actionPerformed(ActionEvent a){
			 String selection = ordertypelist.getSelectedValue();
			 Territory source = myMap.getTerritory((Integer.parseInt(sx.getText())), (Integer.parseInt(sy.getText())));
			 Territory destination = myMap.getTerritory((Integer.parseInt(dx.getText())), (Integer.parseInt(dy.getText())));
			 int amount = Integer.parseInt(num.getText());
			 if (selection == "Move"){
			   Move newmove = new Move(username, source, destination, amount);	 
			   if (newmove.CheckOrder(myMap))
				 if (beginattack)
                   JOptionPane.showMessageDialog(null, "You can't submit move orders anymore!"); //new MessageBox("You can't submit move order anymore!");
				 else {
					 moves.add(newmove);
					 //theClient.GetMap().SetupMap(newmove));
				 }
			 }
			 if (selection == "Attack"){
			   Attack newattack = new Attack(username, source, destination, amount);
			   if (newattack.CheckOrder(myMap)){
			     attacks.add(newattack);
			     beginattack = true;
			   }
			     
			 }	 
		   }
	   }
	    
   }
   
   class Clock extends JPanel{
 	   /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	JLabel currenttime;
 	   int setMinute;
 	   int minute, second;
 	   Timer CountdownTimer;
 	   
 	   public Clock(){
 		   minute = 5;
 		   second = 0;
 		   currenttime = new JLabel(String.valueOf(minute) + ":" + String.valueOf(second));
 	       currenttime.setFont(new Font("serif", Font.BOLD, 28));
 	       this.add(currenttime);
 	   }
 	   
 	   public Clock(int minutes) {
 		   setMinute = minutes;
 		   if(second < 10)
 			  currenttime = new JLabel(String.valueOf(minute) + ":" + "0" + String.valueOf(second));
 		   else
 			   currenttime = new JLabel(String.valueOf(setMinute) + ":" + String.valueOf(second));
 		  currenttime.setFont(new Font("serif", Font.BOLD, 28));
 		  this.add(currenttime);
 	   }
 	   
 	   public void setMinutes(int serverTime) {
 		   setMinute = serverTime;
 		   minute = setMinute;
 		  if(second < 10)
 			  currenttime.setText(String.valueOf(setMinute) + ":" + "0" + String.valueOf(second));
 		   else
 			   currenttime.setText(String.valueOf(setMinute) + ":" + String.valueOf(second));
 	   }
 	   
 	   public void Reset(){
 		   CountdownTimer.cancel();
 		   minute = setMinute;
 		   second = 0;
 		   CountdownTimer.schedule(new Countdown(), 1000);
 	   }
 	   
 	   public void startTimer() {
 	       currenttime.setForeground(myColor); // so I can see what I'm doing
 	       CountdownTimer = new Timer();
 	       CountdownTimer.schedule(new Countdown(), 0, 1000);
 	       System.out.println("startTimer finished processing");
 	   }
 	   
 	   class Countdown extends TimerTask{
 		   public void run(){
 			   orderUI.OrderPanel.submitorderbutton.setEnabled(true);
 			   orderUI.OrderPanel.add.setEnabled(true);
 			   System.out.println("Initial Minute: " + minute);
 			   if (second == 0) {
 				   second = 59;
 				   minute--;
 			   }
 			   else second--;
 			   System.out.println("Minute: " + minute);
 			  System.out.println("Second: " + second);
 			   if(second < 10)
 				   currenttime.setText(String.valueOf(minute) + ":" + "0" + String.valueOf(second));
 			   else
 				   currenttime.setText(String.valueOf(minute) + ":" + String.valueOf(second));
 			   if (minute == 0 && second == 0) {
 				   CountdownTimer.cancel();
 				   JOptionPane.showMessageDialog(null, "Time's up!");
 				   orderUI.OrderPanel.submitorderbutton.setEnabled(false);
 				   orderUI.OrderPanel.add.setEnabled(false);
 			   }
 		   }
 	   }
   }
	
	public static void main(String[] args) throws IOException {
		ClientTest c = new ClientTest();
	}
}